"""
ONVIF服务检测模块
专门用于检测ONVIF设备的服务可用性
"""

import time
import socket
import xml.etree.ElementTree as ET
from datetime import datetime
from typing import Optional, Dict, List, Tuple
from dataclasses import dataclass
import requests
from requests.auth import HTTPDigestAuth, HTTPBasicAuth
from loguru import logger

from ..models.device import DeviceInfo


@dataclass
class ONVIFServiceStatus:
    """ONVIF服务状态"""
    ip_address: str
    port: int
    is_available: bool
    response_time_ms: Optional[float]
    service_endpoint: Optional[str]
    soap_fault: Optional[str]
    http_status_code: Optional[int]
    error_message: str = ""
    timestamp: datetime = None
    
    def __post_init__(self):
        if self.timestamp is None:
            self.timestamp = datetime.now()


class ONVIFServiceChecker:
    """ONVIF服务检测器"""
    
    def __init__(self, timeout: float = 3.0):
        self.timeout = timeout
        
        # 常见的ONVIF服务端点
        self.service_endpoints = [
            "/onvif/device_service",
            "/onvif/Device",
            "/Device",
            "/onvif/device",
            "/onvif/services/device",
            "/cgi-bin/hi3510/onvifdevice.cgi",
            "/onvif1/device_service"
        ]
        
        # 常见的ONVIF端口
        self.common_ports = [80, 8080, 8000, 8899, 554, 8554]
        
        # ONVIF GetSystemDateAndTime SOAP请求模板（轻量级检测）
        self.soap_template = '''<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" 
               xmlns:tds="http://www.onvif.org/ver10/device/wsdl">
    <soap:Header/>
    <soap:Body>
        <tds:GetSystemDateAndTime/>
    </soap:Body>
</soap:Envelope>'''
        
        logger.info("ONVIF服务检测器初始化完成")
    
    def check_tcp_port(self, ip_address: str, port: int, timeout: float = None) -> bool:
        """
        检查TCP端口是否开放
        
        Args:
            ip_address: IP地址
            port: 端口号
            timeout: 超时时间
            
        Returns:
            bool: 端口是否开放
        """
        timeout = timeout or self.timeout
        
        try:
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(timeout)
            result = sock.connect_ex((ip_address, port))
            sock.close()
            return result == 0
        except Exception as e:
            logger.debug(f"TCP端口检测失败 {ip_address}:{port} - {e}")
            return False
    
    def send_soap_request(self, url: str, soap_body: str, timeout: float = None) -> Tuple[bool, Optional[int], Optional[str], Optional[float]]:
        """
        发送SOAP请求
        
        Args:
            url: ONVIF服务URL
            soap_body: SOAP请求体
            timeout: 超时时间
            
        Returns:
            Tuple[bool, Optional[int], Optional[str], Optional[float]]: 
            (是否成功, HTTP状态码, 响应内容, 响应时间毫秒)
        """
        timeout = timeout or self.timeout
        
        headers = {
            'Content-Type': 'application/soap+xml; charset=utf-8',
            'User-Agent': 'ONVIF-Monitor/1.0'
        }
        
        try:
            start_time = time.time()
            response = requests.post(
                url,
                data=soap_body,
                headers=headers,
                timeout=timeout
            )
            response_time = (time.time() - start_time) * 1000
            
            return True, response.status_code, response.text, response_time
            
        except requests.exceptions.Timeout:
            logger.debug(f"SOAP请求超时: {url}")
            return False, None, "请求超时", None
        except requests.exceptions.ConnectionError:
            logger.debug(f"SOAP连接失败: {url}")
            return False, None, "连接失败", None
        except Exception as e:
            logger.debug(f"SOAP请求异常: {url} - {e}")
            return False, None, str(e), None
    
    def parse_soap_response(self, response_text: str) -> Tuple[bool, Optional[str]]:
        """
        解析SOAP响应
        
        Args:
            response_text: 响应文本
            
        Returns:
            Tuple[bool, Optional[str]]: (是否为有效ONVIF响应, SOAP错误信息)
        """
        try:
            root = ET.fromstring(response_text)
            
            # 检查是否包含SOAP Fault
            fault_elements = root.findall('.//{http://www.w3.org/2003/05/soap-envelope}Fault')
            if fault_elements:
                fault_code = fault_elements[0].find('.//{http://www.w3.org/2003/05/soap-envelope}Code')
                fault_reason = fault_elements[0].find('.//{http://www.w3.org/2003/05/soap-envelope}Reason')
                
                fault_info = "SOAP Fault"
                if fault_code is not None:
                    fault_info += f" - Code: {fault_code.text}"
                if fault_reason is not None:
                    fault_info += f" - Reason: {fault_reason.text}"
                
                return True, fault_info  # 有SOAP Fault但仍然是有效的ONVIF响应
            
            # 检查是否包含ONVIF相关的命名空间或元素
            onvif_namespaces = [
                'http://www.onvif.org/ver10/device/wsdl',
                'http://www.onvif.org/ver10/schema',
                'http://www.onvif.org/ver20/ptz/wsdl'
            ]
            
            response_str = response_text.lower()
            if any(ns.lower() in response_str for ns in onvif_namespaces) or 'onvif' in response_str:
                return True, None
            
            # 检查是否包含GetSystemDateAndTimeResponse
            if 'getsystemdateandtimeresponse' in response_str or 'systemdateandtime' in response_str:
                return True, None
            
            return False, "非ONVIF响应"
            
        except ET.ParseError:
            # XML解析失败，但可能是其他格式的有效响应
            response_lower = response_text.lower()
            if 'onvif' in response_lower or 'device_service' in response_lower:
                return True, None
            return False, "XML解析失败"
        except Exception as e:
            logger.debug(f"SOAP响应解析异常: {e}")
            return False, str(e)
    
    def check_onvif_service(self, ip_address: str, port: int = 80, endpoint: str = None) -> ONVIFServiceStatus:
        """
        检查ONVIF服务
        
        Args:
            ip_address: IP地址
            port: 端口号
            endpoint: 服务端点，如果为None则尝试所有常见端点
            
        Returns:
            ONVIFServiceStatus: 服务状态
        """
        logger.debug(f"检查ONVIF服务: {ip_address}:{port}")
        
        # 首先检查TCP端口是否开放
        if not self.check_tcp_port(ip_address, port):
            return ONVIFServiceStatus(
                ip_address=ip_address,
                port=port,
                is_available=False,
                response_time_ms=None,
                service_endpoint=None,
                soap_fault=None,
                http_status_code=None,
                error_message=f"端口 {port} 未开放"
            )
        
        # 如果指定了端点，只检查该端点
        endpoints_to_check = [endpoint] if endpoint else self.service_endpoints
        
        for ep in endpoints_to_check:
            url = f"http://{ip_address}:{port}{ep}"
            
            # 发送SOAP请求
            success, status_code, response_text, response_time = self.send_soap_request(
                url, self.soap_template, self.timeout
            )
            
            if success and status_code:
                # 检查HTTP状态码
                if status_code == 200:
                    # 解析SOAP响应
                    is_onvif, soap_fault = self.parse_soap_response(response_text)
                    
                    if is_onvif:
                        logger.debug(f"ONVIF服务检测成功: {url} - {response_time:.1f}ms")
                        return ONVIFServiceStatus(
                            ip_address=ip_address,
                            port=port,
                            is_available=True,
                            response_time_ms=response_time,
                            service_endpoint=ep,
                            soap_fault=soap_fault,
                            http_status_code=status_code
                        )
                
                elif status_code in [400, 401, 405, 500]:
                    # 这些状态码表明服务存在但可能需要认证或方法不对
                    logger.debug(f"ONVIF服务存在但返回错误: {url} - HTTP {status_code}")
                    return ONVIFServiceStatus(
                        ip_address=ip_address,
                        port=port,
                        is_available=True,
                        response_time_ms=response_time,
                        service_endpoint=ep,
                        soap_fault=None,
                        http_status_code=status_code,
                        error_message=f"HTTP {status_code} - 服务存在但可能需要认证"
                    )
        
        # 所有端点都检查失败
        return ONVIFServiceStatus(
            ip_address=ip_address,
            port=port,
            is_available=False,
            response_time_ms=None,
            service_endpoint=None,
            soap_fault=None,
            http_status_code=None,
            error_message="所有ONVIF端点检查失败"
        )
    
    def quick_onvif_check(self, ip_address: str, port: int = 80) -> bool:
        """
        快速ONVIF服务检查（仅返回是否可用）
        
        Args:
            ip_address: IP地址
            port: 端口号
            
        Returns:
            bool: 服务是否可用
        """
        status = self.check_onvif_service(ip_address, port)
        return status.is_available
    
    def check_multiple_ports(self, ip_address: str, ports: List[int] = None) -> List[ONVIFServiceStatus]:
        """
        检查多个端口的ONVIF服务
        
        Args:
            ip_address: IP地址
            ports: 端口列表，默认使用常见端口
            
        Returns:
            List[ONVIFServiceStatus]: 各端口的服务状态
        """
        ports = ports or self.common_ports
        results = []
        
        for port in ports:
            status = self.check_onvif_service(ip_address, port)
            results.append(status)
            
            # 如果找到可用的服务，可以选择提前返回
            if status.is_available:
                logger.info(f"在端口 {port} 找到ONVIF服务: {ip_address}")
        
        return results
    
    def find_onvif_service(self, ip_address: str) -> Optional[ONVIFServiceStatus]:
        """
        查找设备上的ONVIF服务
        
        Args:
            ip_address: IP地址
            
        Returns:
            Optional[ONVIFServiceStatus]: 找到的服务状态，如果没找到则返回None
        """
        logger.info(f"查找ONVIF服务: {ip_address}")
        
        results = self.check_multiple_ports(ip_address)
        
        # 返回第一个可用的服务
        for result in results:
            if result.is_available:
                logger.info(f"找到ONVIF服务: {ip_address}:{result.port}{result.service_endpoint}")
                return result
        
        logger.warning(f"未找到ONVIF服务: {ip_address}")
        return None
